package cron

import (
	"cloudops/src/common"
	"cloudops/src/models"
	"context"
	"encoding/json"
	"fmt"
	"github.com/gammazero/workerpool"
	"go.uber.org/zap"
	"k8s.io/apimachinery/pkg/util/wait"
	"strings"
	"time"
)

// AutoOrderManager 自动下单
func (cm *CronManager) AutoOrderManager(ctx context.Context) error {
	// 每隔多长时间去执行一次直到完成
	go wait.UntilWithContext(ctx, cm.RunAutoOrder, time.Duration(cm.Sc.WorkOrderAutoActionC.RunIntervalSeconds)*time.Second)
	<-ctx.Done()
	cm.Sc.Logger.Info("AutoOrderManager 收到其他任务退出信号 退出")
	return nil
}

// RunAutoOrder 执行自动下单
func (cm *CronManager) RunAutoOrder(ctx context.Context) {
	start := time.Now()
	serviceAccountName := cm.Sc.WorkOrderAutoActionC.ServiceAccount
	if serviceAccountName == "" {
		cm.Sc.Logger.Error("工单自动执行 未配置服务账号 工单任务退出")
		return
	}
	currentFlowNode := fmt.Sprintf("用户@%v", serviceAccountName)
	pendingOrders, count, err := models.GetWorkOrderInstanceByStatusAndCurrentFlowNode(common.WorkOrderInstanceStatusPendingAction, currentFlowNode)
	if err != nil {
		cm.Sc.Logger.Error("工单自动执行 扫描数据库中待执行的工单失败", zap.Error(err))
		return
	}

	cm.Sc.Logger.Info("工单自动执行 获取待执行工作流实例总数", zap.Int64("数量", count))
	wp := workerpool.New(cm.Sc.WorkOrderAutoActionC.BatchNum)
	for _, pendingOrder := range pendingOrders {
		pendingOrder := pendingOrder
		wp.Submit(func() {
			// 启动任务
			cm.RunAutoOrderOne(pendingOrder)
		})
	}
	// 等待所有任务完成
	wp.StopWait()
	tookSeconds := time.Since(start).Seconds()
	cm.Sc.Logger.Info("扫描数据库中待执行的耗时 执行完成", zap.Float64("耗时(s)", tookSeconds), zap.Int64("数量", count))
}

// RunAutoOrderOne 执行自动下单的一个
func (cm *CronManager) RunAutoOrderOne(pendingOrder *models.WorkOrderInstance) {
	cm.Sc.Logger.Info("开始执行自动执行", zap.String("工单", pendingOrder.Name))
	pendingOrder.FillFrontAllDataWorkOrderInstance()
	template := pendingOrder.Template
	if template == nil {
		return
	}
	switch template.Name {
	case cm.Sc.WorkOrderAutoActionC.AutoTemplateNameBuyEcs:
		cm.RunAutoOrderOneBuyEcs(pendingOrder)
	}
}

func (cm *CronManager) RunAutoOrderOneBuyEcs(pendingOrder *models.WorkOrderInstance) {
	cm.Sc.Logger.Info("开始执行自动下单买Ecs", zap.Any("工单", pendingOrder.Name))
	var ecsBuyWorkOrderReq models.EcsBuyWorkOrder
	err := json.Unmarshal([]byte(pendingOrder.ActualApiJsonData), &ecsBuyWorkOrderReq)
	if err != nil {
		cm.Sc.Logger.Error("自动下单买Ecs 反序列化失败",
			zap.Any("工单数据", pendingOrder.ActualApiJsonData),
			zap.Any("工单名称", pendingOrder.Name),
			zap.Error(err))
		return
	}

	hostNames := strings.Split(ecsBuyWorkOrderReq.Hostnames, "\n")

	// 获取服务树节点的ID
	dbNode, err := models.GetStreeNodeById(ecsBuyWorkOrderReq.BindLeafNodeId)
	if err != nil {
		cm.Sc.Logger.Error("自动下单买Ecs 获取服务树节点失败",
			zap.Any("树节点", ecsBuyWorkOrderReq.BindLeafNodeId),
			zap.Error(err))
		return
	}

	// 开始下单
	success := true
	successNum := 0
	failNum := 0
	successHosts := []string{}
	failedHosts := []string{}
	errs := []error{}
	// 做数据模拟 线上环境需要注释掉
	for index, hostName := range hostNames {
		objOne := MockEcsDescribeEcsOne(hostName, fmt.Sprintf("uuid-%v", hostName), index, true)
		// 创建资源
		err = objOne.CreateOne()
		if err != nil {
			cm.Sc.Logger.Error("自动下单买Ecs 创建资源失败",
				zap.Any("树节点", ecsBuyWorkOrderReq.BindLeafNodeId),
				zap.Any("主机名", hostName),
				zap.Any("工单名称", pendingOrder.Name),
				zap.Error(err))
			errs = append(errs, err)
			success = false
			failNum++
			failedHosts = append(failedHosts, fmt.Sprintf("name:%v 错误:%v\n", hostName, err))
			continue
		}

		// 绑定服务树节点
		objOne.BindNodes = append(objOne.BindNodes, dbNode)
		err = objOne.UpdateBindNodes(objOne.BindNodes)
		if err != nil {
			success = false
			failNum++
			failedHosts = append(failedHosts, fmt.Sprintf("name:%v 错误:%v\n", hostName, err))
			cm.Sc.Logger.Error("自动下单买Ecs 绑定服务树节点失败",
				zap.Any("树节点", ecsBuyWorkOrderReq.BindLeafNodeId),
				zap.Any("主机名", hostName),
				zap.Any("工单名称", pendingOrder.Name),
				zap.Error(err))
			continue
		}
		cm.Sc.Logger.Info("自动下单买Ecs 完成",
			zap.Any("树节点", ecsBuyWorkOrderReq.BindLeafNodeId),
			zap.Any("主机名", hostName),
			zap.Any("工单名称", pendingOrder.Name))
		successNum++
		successHosts = append(successHosts, hostName)
	}
	outPut := fmt.Sprintf("总数:%v 成功:%v 失败%v\n"+
		"成功列表:%v\n"+
		"失败详情:%v",
		len(hostNames),
		successNum,
		failNum,
		strings.Join(successHosts, ","),
		strings.Join(failedHosts, ","),
	)

	// 更新工单状态
	pendingOrder.Status = common.WorkOrderInstanceStatusFinished

	var actualFlowData []models.FlowNode
	err = json.Unmarshal([]byte(pendingOrder.ActualFlowData), &actualFlowData)
	if err != nil {
		cm.Sc.Logger.Error("自动下单买Ecs 解析时间轴数据json失败",
			zap.Any("树节点", ecsBuyWorkOrderReq.BindLeafNodeId),
			zap.Any("工单名称", pendingOrder.Name),
			zap.Error(err))
		return
	}

	index := len(actualFlowData) - 1
	node := actualFlowData[index]
	node.EndTime = time.Now().Format("2006-01-02 15:04:05")
	node.IsPassOrIsSuccess = success
	node.OutPut = outPut

	actualFlowData[index] = node
	flowNodeStr, _ := json.Marshal(actualFlowData)
	pendingOrder.ActualFlowData = string(flowNodeStr)
	err = pendingOrder.UpdateOneWorkOrderInstance()
	if err != nil {
		cm.Sc.Logger.Error("自动下单买Ecs 更新工单状态失败",
			zap.Any("工单名称", pendingOrder.Name),
			zap.Error(err))
		return
	}
	cm.Sc.Logger.Info("自动下单买Ecs 完成",
		zap.Any("树节点", ecsBuyWorkOrderReq.BindLeafNodeId),
		zap.Any("工单名称", pendingOrder.Name))
}
